package com.java8_training;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import com.javase.entity.User;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @author taoshen
 * @create 2021/9/23
 */
public class TAOTAOJava8 {
    @Test
    public void test1() {
        List<User> userList = CollUtil.newArrayList();

        IntStream.rangeClosed(1, 10).forEach(i -> {
            User user = new User();
            user.setName(IdUtil.simpleUUID().substring(0, 5));
            user.setId(i);
            user.setGender(i % 2 == 0 ? 1 : 2);
            userList.add(user);
        });

        /**
         * 将集合分为两个集合：男一个集合  女一个集合
         */
        // 男集合
        List<User> list1 = userList.stream().filter(u -> u.getGender() == 1).collect(Collectors.toList());
        // 女集合
        List<User> list2 = userList.stream().filter(u -> u.getGender() == 2).collect(Collectors.toList());


        /**
         * 将集合收集成一个Map  键为字符串 男或者女  值为对应的所有id的 使用逗号拼接的字符串
         * ["男", "1, 3, 5"]
         * ["女", "2, 4, 6"]
         */
        Map<String, String> map = MapUtil.newHashMap();
        // 将id通过map给抽取出来的同时转换成String类 就可以使用Collectors.joining()拼接字符串了
        // public static Collector<CharSequence, ?, String> joining() {}
        // joining设置泛型为String 只能玩字符串 所以把Integer型的id给封装成String型再玩就行了
        map.put("男", list1.stream().map(u -> String.valueOf(u.getId())).collect(Collectors.joining(",")));
        map.put("女", list2.stream().map(u -> String.valueOf(u.getId())).collect(Collectors.joining(",")));
    }

    @Test
    public void test2() {
        List<User> userList = CollUtil.newArrayList();
        User user1 = new User();
        user1.setId(1).setName("张翠山").setPhone("13333333333");
        userList.add(user1);

        User user2 = new User();
        user2.setId(2).setName("张无忌").setPhone("13333333333");
        userList.add(user2);

        User user3 = new User();
        user3.setId(3).setName("东方不败").setPhone("1333333");
        userList.add(user3);

        User user4 = new User();
        user4.setId(4).setName("张角").setPhone("33333333333");
        userList.add(user4);

        /**
         * 分别筛选出：
         * 1.姓张的
         * 2.名字三个字的
         * 3.手机号号码合法的  号码由1开头  并且全是11位数字 则判定合法 尽量使用正则去regex
         */

        // 姓张的
        List<User> list1 = userList.stream().filter(u -> u.getName().startsWith("张")).collect(Collectors.toList());

        // 名字三个字
        List<User> list2 = userList.stream().filter(u -> u.getName().length() == 3).collect(Collectors.toList());

        // 手机号合法的
        List<User> list3 = userList.stream().filter(u -> ReUtil.isMatch("^1\\d{10}$", u.getPhone())).collect(Collectors.toList());

        // &&具有短路的特性 如果前面的条件已经不满足了 后面就直接不走了 所以先判断简单的 再判断 难的耗时的逻辑
        // 这里先判断最简单的长度 如果不满足 就不走了 再判断稍微损耗大的startWith 如果不满足 则不走了 最后再判断最耗时最复杂的逻辑
        List<User> list4 = userList.stream()
                .filter(u -> u.getPhone().length() == 11 && u.getPhone().startsWith("1") && NumberUtil.isNumber(u.getPhone()))
                .collect(Collectors.toList());

        /**
         * 去除手机号不合法的用法  剩下的用户姓名以及手机号加密
         * 加密规则：
         * 13333333333 =》 133****3333
         * 张翠山 =》 张**
         * 张角 =》 张**
         * 东方不败 =》 东**
         */
//        List<User> list5 = userList.stream().filter(u -> ReUtil.isMatch("^1\\d{10}$", u.getPhone()))
//                .map(u -> {
//                    u.setPhone(u.getPhone().substring(0, 3) + "****" + u.getPhone().substring(7))
//                            .setName(u.getName().charAt(0) + "**");
//                    return u;
//                }).collect(Collectors.toList());

        /**
         * 因为User实体类中是设置了 lombok提供的 @Accessors(chain = true)
         * 所以每个setter getter函数最终都是返回this 也就是实体类本身
         * 则上述写法 再setter的时候 可以链式调用 并且最终还是return this的 刚好就是我们想要的期望值
         * 所以可以再次优雅细节一点
         */
        List<User> list6 = userList.stream().filter(u -> ReUtil.isMatch("^1\\d{10}$", u.getPhone()))
                .map(u -> u.setPhone(u.getPhone().substring(0, 3) + "****" + u.getPhone().substring(7))
                        .setName(u.getName().charAt(0) + "**")
                ).collect(Collectors.toList());
    }

    @Test
    public void test3(){
        List<User> list = new ArrayList<>();

        User user1 = new User();
        user1.setName("张三").setAddress("合肥");
        list.add(user1);

        User user2 = new User();
        user2.setName("王五").setAddress("延边");
        list.add(user2);

        User user3 = new User();
        user3.setName("赵六").setAddress("齐齐哈尔");
        list.add(user3);

        User user4 = new User();
        user4.setName("李四").setAddress("合肥");
        list.add(user4);

        /**
         * 统计地址为"合肥"的人数
         */
        // 只是单纯的统计集合中 满足某一个条件下的元素的个数而已 先filter筛选掉不合法的 再count收尾
        System.out.println(list.stream().filter(u -> "合肥".equals(u.getAddress())).count());
    }


    @Test
    public void test4() {
        List<User> list = CollUtil.newArrayList();

        IntStream.rangeClosed(1, RandomUtil.randomInt(4, 20)).forEach(i -> {
            User user = new User();
            user.setId(i);
            user.setName(RandomUtil.randomString(4));
            list.add(user);
        });

        /**
         * 通过用户的name按照ASCII码表进行排序 再统计出top10
         * 如果集合中有 >= 10个用户 则取出前10个
         * 如果集合中 没有10个用户 则有多少 取出多少
         */
        List<User> list1 = list.stream()
                .sorted((u1, u2) -> u1.getName().compareTo(u2.getName()))
                .collect(Collectors.toList());
    }

    @Test
    public void test5() {
        List<User> list = CollUtil.newArrayList();
        // 因为User实体类有lombok的@Accessors(chain = true) 每次都会return this 所以可以如此写法
        list.add(new User().setId(1).setHobbies("足球,篮球,网球,台球"));
        list.add(new User().setId(2).setHobbies("足球,篮球,网球"));
        list.add(new User().setId(3).setHobbies("篮球,网球,台球"));
        list.add(new User().setId(4).setHobbies("网球,台球"));
        list.add(new User().setId(5).setHobbies("足球,篮球,台球"));

        /**
         * 四种球类：足球,篮球,网球,台球
         *
         * 统计出喜欢各种球类的分别有多少人 搞成一个Map<String, Integer>
         * ["足球":3]
         * ["篮球":4]
         */
        Map<String, Integer> map1 = MapUtil.newHashMap();
        map1.put("足球", (int) list.stream().filter(u -> u.getHobbies().contains("足球")).count());
        map1.put("篮球", (int) list.stream().filter(u -> u.getHobbies().contains("篮球")).count());
        map1.put("网球", (int) list.stream().filter(u -> u.getHobbies().contains("网球")).count());
        map1.put("台球", (int) list.stream().filter(u -> u.getHobbies().contains("台球")).count());


        /**
         * 进阶：
         * 如果不知道 到底有多少种球类呢 有什么就分析什么  怎么实现
         */

        /**
         * 第一种写法 先将所有人的所有爱好都逗号拼接起来 然后再用逗号切割 就得到所有人的所有爱好的集合  再去个重 就得到 一共出现了哪些球类
         * 然后再执行循环 执行上述的contains写法  全程就不需要知道 他有哪些球类
         */
        Map<String, Integer> map2 = MapUtil.newHashMap();
        String[] hobbyArray = list.stream().map(u -> u.getHobbies()).collect(Collectors.joining(",")).split(",");
        Stream.of(hobbyArray).distinct()
                .forEach(hobby -> map2.put(hobby, (int) list.stream().filter(u -> u.getHobbies().contains(hobby)).count()));


        /**
         * 第二种写法  所有人的所有爱好逗号拼接起来 再切割成集合  然后直接利用Map的特性 进行计数成Map<String, Integer>
         * Map的getOrDefault函数 语义为从Map集合根据键获取值 如果没有这个键 则返回给定默认值
         * 所以可以利用getOrDefault取代 if else 中的else给默认值环节
         */
        Map<String, Integer> map3 = MapUtil.newHashMap();
        /*
        Stream.of(list.stream().map(u -> u.getHobbies()).collect(Collectors.joining(",")).split(","))
                .forEach(hobby -> {
                    if (map3.containsKey(hobby)) {
                        map3.put(hobby, map3.get(hobby) + 1);
                    } else {
                        map3.put(hobby, 1);
                    }
                });
        */

        Stream.of(list.stream().map(u -> u.getHobbies()).collect(Collectors.joining(",")).split(","))
                .forEach(hobby -> map3.put(hobby, map3.getOrDefault(hobby, 0) + 1));
    }

    @Test
    public void test6() {
        List<User> list = CollUtil.newArrayList();

        IntStream.rangeClosed(1, 100).forEach(i -> {
            list.add(new User().setId(i).setHobbies(RandomUtil.randomEle(Arrays.asList("足球", "篮球", "网球", "台球"))));
        });

        /**
         * 分析出喜欢各种球类 各有多少人 收集成Map<String, Integer>
         * 再根据人数 进行rank排行  最终返回top3的Map<String, Integer>
         */
        // 用来计数的map
        Map<String, Integer> map = MapUtil.newHashMap();
        list.stream().map(u -> u.getHobbies()).forEach(hobby -> map.put(hobby, map.getOrDefault(hobby, 0) + 1));

        // 有序的map
        Map<String, Integer> linkedMap = new LinkedHashMap();
        map.entrySet().stream().sorted((entry1, entry2) -> entry2.getValue() - entry1.getValue())
                .limit(3).forEach(entry -> linkedMap.put(entry.getKey(), entry.getValue()));

        linkedMap.forEach((k, v) -> System.out.println(k + " : " + v));
    }

    @Test
    public void test7() {
        List<User> list = CollUtil.newArrayList();
        list.add(new User().setId(1).setHobbies("足球,篮球,网球"));
        list.add(new User().setId(2).setHobbies("台球,篮球,网球"));
        list.add(new User().setId(3).setHobbies("足球,篮球"));
        list.add(new User().setId(4).setHobbies("足球"));
        // 随机两个球类
        String randomHobbies = RandomUtil.randomEle(Arrays.asList("足球", "篮球", "网球", "台球")) + ","
                + RandomUtil.randomEle(Arrays.asList("足球", "篮球", "网球", "台球"));

        /**
         * 这些同学  最近又都一起参加了随机两种球类（足球,篮球,网球,羽毛球的其中任意两种）
         * 对这些同学的hobbies字段进行更新
         *
         * 例如：随机到 足球和网球
         * 则 1同学 还是 足球,篮球,网球
         * 则 2同学 是 台球,篮球,网球,足球
         *    。。
         *    。。
         */
        list.stream().forEach(u -> u.setHobbies(Stream.of((u.getHobbies() + "," + randomHobbies).split(","))
                .distinct().collect(Collectors.joining(","))));

        list.forEach(u -> System.out.println(u.getHobbies()));
    }

    @Test
    public void test8 () {
        List<User> list = CollUtil.newArrayList();
        list.add(new User().setId(1).setGender(1).setHobbies("足球,篮球,网球"));
        list.add(new User().setId(2).setGender(2).setHobbies("足球,篮球,网球"));
        list.add(new User().setId(3).setGender(1).setHobbies("足球,台球,网球"));
        list.add(new User().setId(4).setGender(2).setHobbies("足球,篮球,网球,羽毛球"));
        list.add(new User().setId(5).setGender(1).setHobbies("足球,网球"));
        list.add(new User().setId(6).setGender(2).setHobbies("铅球"));

        /**
         * 统计出一个Map<String, String> 键为：男或者女 值为：喜欢各个球类背后有哪些人 把id体现出来
         * 例如：
         * ["男" : "足球:1,2,3&&篮球:1,3"]
         * ["女" : "足球:1,2,3&&网球:1,3"&&铅球:1]
         */
        Map<String, String> manAndWomanMap = MapUtil.newHashMap();

        /**
         * 男集合
         */
        List<User> manList = list.stream().filter(u -> u.getGender() == 1).collect(Collectors.toList());
        Map<String, String> hobbyMap = MapUtil.newHashMap();

        // 男集合中总共出现过哪些爱好
        String manHobbies = manList.stream().map(u -> u.getHobbies()).collect(Collectors.joining(","));

        Stream.of(manHobbies.split(",")).distinct().forEach(hobby -> {
            String hobbyIds = manList.stream().filter(u -> u.getHobbies().contains(hobby))
                    .map(u -> String.valueOf(u.getId())).collect(Collectors.joining(","));

            hobbyMap.put(hobby, hobbyIds);
        });

        List<String> hobbyList = CollUtil.newArrayList();
        hobbyMap.forEach((k, v) -> hobbyList.add(k + ":" + v));
        manAndWomanMap.put("男", hobbyList.stream().collect(Collectors.joining("&&")));

        /**
         * 女集合
         */
        List<User> womanList = list.stream().filter(u -> u.getGender() == 2).collect(Collectors.toList());
        Map<String, String> womanHobbyMap = MapUtil.newHashMap();

        // 女集合中总共出现过哪些爱好
        String womanHobbies = womanList.stream().map(u -> u.getHobbies()).collect(Collectors.joining(","));

        Stream.of(womanHobbies.split(",")).distinct().forEach(hobby -> {
            String hobbyIds = womanList.stream().filter(u -> u.getHobbies().contains(hobby))
                    .map(u -> String.valueOf(u.getId())).collect(Collectors.joining(","));

            womanHobbyMap.put(hobby, hobbyIds);
        });

        List<String> womanHobbyList = CollUtil.newArrayList();
        womanHobbyMap.forEach((k, v) -> womanHobbyList.add(k + ":" + v));
        manAndWomanMap.put("女", womanHobbyList.stream().collect(Collectors.joining("&&")));

        manAndWomanMap.forEach((k, v) -> System.out.println(k + " : " + v));




        /**
         * 统计出Map<String, String> 键为：各种出现的球类 值为：该球类下 男有哪些人 女有哪些人 把id体现出来
         * 如果 某球类下 某个性别没有人 则表示为-1代表没有
         * 例如：
         * ["足球" : "男:1,2,3&&女:1,3"]
         * ["铅球" : "男:-1&&女:6"]
         */
        // 所有人 不管男女 出现过哪些球类
        List<String> allHobbyList = Stream.of(list.stream().map(u -> u.getHobbies()).collect(Collectors.joining(","))
                .split(",")).distinct().collect(Collectors.toList());

        Map<String, String> allManHobbyMap = MapUtil.newHashMap();

        allHobbyList.forEach(hobby -> {
            /**
             * 男集合
             */
            String userIds = list.stream().filter(u -> u.getGender() == 1 && u.getHobbies().contains(hobby))
                    .map(u -> String.valueOf(u.getId())).collect(Collectors.joining(","));

            /**
             * 女集合
             */
            String userIds1 = list.stream().filter(u -> u.getGender() == 2 && u.getHobbies().contains(hobby))
                    .map(u -> String.valueOf(u.getId())).collect(Collectors.joining(","));

            // StringBuilder中的append 也是return this 可以链式调用
            StringBuilder manAndWomanHobby = StrUtil.builder();
            manAndWomanHobby
                    .append("男:")
                    .append(StrUtil.isNotEmpty(userIds) ? userIds : "-1")
                    .append("&&")
                    .append("女:")
                    .append(StrUtil.isNotEmpty(userIds1) ? userIds1 : "-1");

            allManHobbyMap.put(hobby, manAndWomanHobby.toString());
        });

        allManHobbyMap.forEach((k, v) -> System.out.println(k + " : " + v));
    }
}
